/*
 * Copyright 2012-present the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.buildpack.platform.docker.configuration;

import java.util.function.BiConsumer;

import org.jspecify.annotations.Nullable;

import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
import org.springframework.util.Assert;

/**
 * Docker registry authentication configuration.
 *
 * @author Scott Frederick
 * @since 2.4.0
 */
@FunctionalInterface
public interface DockerRegistryAuthentication {

	/**
	 * An empty {@link #user(String, String, String, String)} authentication.
	 * @since 3.5.0
	 */
	DockerRegistryAuthentication EMPTY_USER = DockerRegistryAuthentication.user("", "", "", "");

	/**
	 * Returns the auth header that should be used for docker authentication for the given
	 * image reference.
	 * @param imageReference the image reference or {@code null}
	 * @return the auth header
	 * @since 3.5.0
	 */
	default @Nullable String getAuthHeader(@Nullable ImageReference imageReference) {
		return getAuthHeader();
	}

	/**
	 * Returns the auth header that should be used for docker authentication.
	 * @return the auth header
	 */
	@Nullable String getAuthHeader();

	/**
	 * Factory method to that returns a new {@link DockerRegistryAuthentication} instance
	 * that uses a header generated by base64 encoding a JSON payload created from the
	 * given parameters.
	 * @param identityToken the identity token JSON field
	 * @return a new {@link DockerRegistryAuthentication} instance
	 * @since 3.5.0
	 */
	static DockerRegistryAuthentication token(String identityToken) {
		return new DockerRegistryTokenAuthentication(identityToken);
	}

	/**
	 * Factory method to that returns a new {@link DockerRegistryAuthentication} instance
	 * that uses a header generated by base64 encoding a JSON payload created from the
	 * given parameters.
	 * @param username the username JSON field
	 * @param password the password JSON field
	 * @param serverAddress the server address JSON field
	 * @param email the email JSON field
	 * @return a new {@link DockerRegistryAuthentication} instance
	 * @since 3.5.0
	 */
	static DockerRegistryAuthentication user(String username, String password, @Nullable String serverAddress,
			@Nullable String email) {
		return new DockerRegistryUserAuthentication(username, password, serverAddress, email);
	}

	/**
	 * Factory method that returns a new {@link DockerRegistryAuthentication} instance
	 * that uses the standard docker JSON config (including support for credential
	 * helpers) to generate auth headers.
	 * @param fallback the fallback authentication to use if no suitable config is found,
	 * may be {@code null}
	 * @return a new {@link DockerRegistryAuthentication} instance
	 * @since 3.5.0
	 * @see #configuration(DockerRegistryAuthentication, BiConsumer)
	 */
	static DockerRegistryAuthentication configuration(@Nullable DockerRegistryAuthentication fallback) {
		return configuration(fallback, (message, ex) -> System.out.println(message));
	}

	/**
	 * Factory method that returns a new {@link DockerRegistryAuthentication} instance
	 * that uses the standard docker JSON config (including support for credential
	 * helpers) to generate auth headers.
	 * @param fallback the fallback authentication to use if no suitable config is found,
	 * may be {@code null}
	 * @param credentialHelperExceptionHandler callback that should handle credential
	 * helper exceptions, never {@code null}
	 * @return a new {@link DockerRegistryAuthentication} instance
	 * @since 3.5.0
	 * @see #configuration(DockerRegistryAuthentication, BiConsumer)
	 */
	static DockerRegistryAuthentication configuration(@Nullable DockerRegistryAuthentication fallback,
			BiConsumer<String, Exception> credentialHelperExceptionHandler) {
		Assert.notNull(credentialHelperExceptionHandler, () -> "'credentialHelperExceptionHandler' must not be null");
		return new DockerRegistryConfigAuthentication(fallback, credentialHelperExceptionHandler);
	}

}
